﻿//
// GrepEngine
//
// Copyright (C) 2011 - Tatsuya Niioka
//
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace GrepEngine
{
    /// <summary>
    /// Grepクラス
    /// </summary>
    public class Grep
    {
        #region Description
        private string m_description;
        /// <summary>
        /// 概要を取得または設定します。
        /// </summary>
        public string Description
        {
            get { return m_description; }
            set { m_description = value; }
        }
        #endregion

        #region Path
        private string m_path;
        /// <summary>
        /// パスを取得または設定します。
        /// </summary>
        public string Path
        {
            get { return m_path; }
            set { m_path = value; }
        }
        #endregion

        #region SearchPattern
        private string m_searchPattern;
        /// <summary>
        /// 検索パターンを取得または設定します。
        /// </summary>
        public string SearchPattern
        {
            get { return m_searchPattern; }
            set { m_searchPattern = value; }
        }
        #endregion

        #region InputEncodingName
        private InputEncodingMode m_inputEncodingName = InputEncodingMode.Auto;
        /// <summary>
        /// 入力エンコーディング名を取得または設定します。
        /// </summary>
        public InputEncodingMode InputEncodingName
        {
            get { return m_inputEncodingName; }
            set { m_inputEncodingName = value; }
        }
        #endregion

        #region OutputEncodingName
        private OutputEncodingMode m_outputEncodingName = OutputEncodingMode.None;
        /// <summary>
        /// 出力エンコーディング名を取得または設定します。
        /// </summary>
        public OutputEncodingMode OutputEncodingName
        {
            get { return m_outputEncodingName; }
            set { m_outputEncodingName = value; }
        }
        #endregion

        #region SearchOption
        private SearchOption m_searchOption = SearchOption.AllDirectories;
        /// <summary>
        /// 検索オプションを取得または設定します。
        /// </summary>
        public SearchOption SearchOption
        {
            get { return m_searchOption; }
            set { m_searchOption = value; }
        }
        #endregion

        #region SearchValue
        private string m_searchValue;
        /// <summary>
        /// 検索文字列を取得または設定します。
        /// </summary>
        public string SearchValue
        {
            get { return m_searchValue; }
            set { m_searchValue = value; }
        }
        #endregion

        #region ReplacementValue
        private string m_replacementValue;
        /// <summary>
        /// 置換文字列を取得または設定します。
        /// </summary>
        public string ReplacementValue
        {
            get { return m_replacementValue; }
            set { m_replacementValue = value; }
        }
        #endregion

        #region OutputLineEnd
        private OutputLineEnd m_outputLineEnd;
        /// <summary>
        /// 出力改行コードを取得または設定します。
        /// </summary>
        public OutputLineEnd OutputLineEnd
        {
            get { return m_outputLineEnd; }
            set { m_outputLineEnd = value; }
        }
        #endregion

        #region Mode
        private GrepMode m_mode = GrepMode.Grep;
        /// <summary>
        /// 処理モードを取得または設定します。
        /// </summary>
        public GrepMode Mode
        {
            get { return m_mode; }
            set { m_mode = value; }
        }
        #endregion

        #region IsIgnoreCase
        private bool m_isIgnoreCase = false;
        /// <summary>
        /// 大文字小文字区別を取得または設定します。
        /// </summary>
        public bool IsIgnoreCase
        {
            get { return m_isIgnoreCase; }
            set { m_isIgnoreCase = value; }
        }
        #endregion

        #region IsWord
        private bool m_isWord = false;
        /// <summary>
        /// ワードオプションを取得または設定します。
        /// </summary>
        public bool IsWord
        {
            get { return m_isWord; }
            set { m_isWord = value; }
        }
        #endregion

        #region IsConditions
        private bool m_conditions;
        /// <summary>
        /// 検索条件状態を取得または設定します。
        /// </summary>
        public bool IsConditions
        {
            get { return m_conditions; }
            set { m_conditions = value; }
        }
        #endregion

        #region ConditionsMode
        private ConditionsMode m_conditionsMode = ConditionsMode.Regex;
        /// <summary>
        /// 検索条件モードを取得または設定します。
        /// </summary>
        public ConditionsMode ConditionsMode
        {
            get
            {
                return m_conditionsMode;
            }
            set
            {
                m_conditionsMode = value;
            }
        }
        #endregion

        #region IsRepeart
        private bool m_isRepeart = false;
        /// <summary>
        /// 反復処理状態を取得または設定します。
        /// </summary>
        public bool IsRepeart
        {
            get { return m_isRepeart; }
            set { m_isRepeart = value; }
        }
        #endregion

        #region IndentConvertMode
        private IndentConvertMode m_indentConvertMode = IndentConvertMode.Space;
        /// <summary>
        /// インデント変換モードを取得または設定します。
        /// </summary>
        public IndentConvertMode IndentConvertMode
        {
            get { return m_indentConvertMode; }
            set { m_indentConvertMode = value; }
        }
        #endregion

        #region TabSize
        private int m_tabSize = 3;
        /// <summary>
        /// タブサイズを取得または設定します。
        /// </summary>
        public int TabSize
        {
            get { return m_tabSize; }
            set { m_tabSize = value; }
        }
        #endregion

        #region IsOutputPath
        private bool m_isOutputPath;
        /// <summary>
        /// 出力先変更状態を取得または設定します。
        /// </summary>
        public bool IsOutputPath
        {
            get { return m_isOutputPath; }
            set { m_isOutputPath = value; }
        }
        #endregion

        #region OutputPath
        private string m_outputPath;
        /// <summary>
        /// 出力フォルダを取得または設定します。
        /// </summary>
        public string OutputPath
        {
            get { return m_outputPath; }
            set { m_outputPath = value; }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// 新しい<see cref="Grep"/>クラスを生成します。
        /// </summary>
        public Grep()
        {
        }
        /// <summary>
        /// 指定したGrep情報で新しい<see cref="Grep"/>クラスを生成します。
        /// </summary>
        /// <param name="info">Grep情報</param>
        public Grep(GrepInfo info)
        {
            Description = info.Description;
            Path = info.Path;
            InputEncodingName = info.InputEncodingName;
            OutputEncodingName = info.OutputEncodingName;
            SearchPattern = info.SearchPattern;
            SearchOption = info.SearchOption;
            SearchValue = info.SearchValue;
            ReplacementValue = info.ReplacementValue;
            OutputLineEnd= info.OutputLineEnd;
            IsWord = info.IsWord;
            IsConditions = info.IsConditions;
            ConditionsMode = info.ConditionsMode;
            IsRepeart = info.IsRepeart;
            IsIgnoreCase = info.IsIgnoreCase;
            Mode = info.Mode;
            IndentConvertMode = info.IndentConvertMode;
            TabSize = info.TabSize;
            IsOutputPath = info.IsOutputPath;
            OutputPath = info.OutputPath;
        }
        #endregion

        #region InputEncoding
        private Encoding m_inputEncoding;
        /// <summary>
        /// 入力エンコーディングを取得または設定します。
        /// </summary>
        internal Encoding InputEncoding
        {
            get { return m_inputEncoding; }
            set { m_inputEncoding = value; }
        }
        #endregion

        #region OutputEncoding
        private Encoding m_outputEncoding = null;
        /// <summary>
        /// 出力エンコーディングを取得または設定します。
        /// </summary>
        internal Encoding OutputEncoding
        {
            get { return m_outputEncoding; }
            set { m_outputEncoding = value; }
        }
        #endregion

        #region FileCount
        private int m_fileCount;
        /// <summary>
        /// ファイル数を取得または設定します。
        /// </summary>
        public int FileCount
        {
            get { return m_fileCount; }
            set { m_fileCount = value; }
        }
        #endregion

        #region CancellationPending
        private bool m_cancellationPending;
        /// <summary>
        /// バックグラウンド操作のキャンセルを要求したかどうかを示す値を取得します。 
        /// </summary>
        internal bool CancellationPending
        {
            get { return m_cancellationPending; }
            set { m_cancellationPending = value; }
        }
        #endregion

        #region Cancel
        /// <summary>
        /// 保留中のバックグラウンド操作のキャンセルを要求します。 
        /// </summary>
        public void Cancel()
        {
            CancellationPending = true;
        }
        #endregion

        #region Run
        /// <summary>
        /// 実行します。
        /// </summary>
        public void Run()
        {
            FileCount = 0;
            var inputFiles = Directory.EnumerateFiles(Path, "*.*", SearchOption);
            List<Regex> patternRegexs = new List<Regex>();
            var searchPatterns = SearchPattern.Split(new char[] { ';' });
            foreach (var pattern in searchPatterns)
            {
                string regex = "^" + GrepHelper.GetWildCardRegex(pattern) + "$";
                patternRegexs.Add(new Regex(regex, RegexOptions.IgnoreCase));
            }
            CancellationTokenSource cts = new CancellationTokenSource();

            ParallelOptions po = new ParallelOptions();
            po.CancellationToken = cts.Token;
            po.MaxDegreeOfParallelism = -1; // System.Environment.ProcessorCount;
            try
            {
                Parallel.ForEach(inputFiles, po, (inputFile) =>
                {
                    if (CancellationPending)
                    {
                        cts.Cancel();
                    }
                    foreach (Regex rex in patternRegexs)
                    {
                        if (rex.IsMatch(inputFile))
                        {
                            RaiseProgressFile(new ProgressEventArgs(inputFile));
                            FileCount++;
                            Run(Path, inputFile);
                            //po.CancellationToken.ThrowIfCancellationRequested();
                            break;
                        }
                    }
                });
                RaiseCompleted(new CompletedEventArgs(FileCount));
            }
            catch(OperationCanceledException ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }
        #endregion

        #region GetInputEncoding
        /// <summary>
        /// 指定した入力ファイル、入力エンコーディングモードでエンコーディングを取得します。
        /// </summary>
        /// <param name="inputFile">入力ファイル</param>
        /// <param name="mode">入力エンコーディングモード</param>
        /// <returns>エンコーディング</returns>
        internal static Encoding GetInputEncoding(string inputFile, InputEncodingMode mode)
        {
            #region InputEncoding
            if (mode == InputEncodingMode.Auto)
            {
                Encoding encoding = FileHelper.GetEncoding(inputFile);
                if (encoding == null)
                {
                    return Encoding.UTF8;
                }
                else
                {
                    return encoding;
                }
            }
            else
            {
                return Encoding.GetEncoding((int)mode);
            }
            #endregion

        }
        #endregion

        #region GetOutputFile
        /// <summary>
        /// 指定した入力フォルダ、入力ファイル、出力先変更、出力フォルダで、出力ファイル名を取得します。
        /// </summary>
        /// <param name="inputPath">入力フォルダ</param>
        /// <param name="inputFile">入力ファイル</param>
        /// <param name="isOutputPath">出力先変更</param>
        /// <param name="outputPath">出力フォルダ</param>
        /// <returns>出力ファイル</returns>
        internal static string GetOutputFile(string inputPath, string inputFile, bool isOutputPath, string outputPath)
        {
            if (isOutputPath)
            {
                DirectoryInfo inDir = new DirectoryInfo(inputPath);
                FileInfo inFile = new FileInfo(inputFile);
                DirectoryInfo outDir = new DirectoryInfo(outputPath);
                return inFile.FullName.Replace(inDir.Name, outDir.Name);
            }
            else
            {
                return inputFile;
            }
        }
        #endregion

        #region GetOutputLineEnd
        /// <summary>
        /// 指定した入力改行コード、出力改行モードから改行コードを取得します。
        /// </summary>
        /// <param name="inputLineEnd">入力改行コード</param>
        /// <param name="outputLineEnd">出力改行モード</param>
        /// <returns>改行コード</returns>
        internal static string GetOutputLineEnd(string inputLineEnd, OutputLineEnd outputLineEnd)
        {
            if (outputLineEnd == OutputLineEnd.None)
            {
                return inputLineEnd;
            }
            else if (outputLineEnd == OutputLineEnd.CRLF)
            {
                return "\r\n";
            }
            else if (outputLineEnd == OutputLineEnd.CR)
            {
                return "\r";
            }
            else if (outputLineEnd == OutputLineEnd.LF)
            {
                return "\n";
            }
            else
            {
                throw new ArgumentException("outputLineEnd");
            }
        }
        #endregion

        #region Regex
        private Regex m_regex = null;
        /// <summary>
        /// 正規表現を取得または設定します。
        /// </summary>
        internal Regex Regex
        {
            get { return m_regex; }
            set { m_regex = value; }
        }
        #endregion

        #region CacheReplacementValue
        private string m_cacheReplacementValue = null;
        /// <summary>
        /// キャッシュ置き換え文字列を取得します。
        /// </summary>
        public string CacheReplacementValue
        {
            get { return m_cacheReplacementValue; }
            set { m_cacheReplacementValue = value; }
        }
        #endregion

        #region Run
        /// <summary>
        /// 指定した入力フォルダ、入力ファイル名、正規表現で実行します。
        /// </summary>
        /// <param name="path">入力フォルダ</param>
        /// <param name="inputFile">入力ファイル名</param>
        public void Run(string path, string inputFile)
        {
            if (InputEncoding == null)
            {
                InputEncoding = GetInputEncoding(inputFile, InputEncodingName);
            }

            string inputLineEnd = FileHelper.GetLineEnd(inputFile, InputEncoding);

            string outputFile = GetOutputFile(path, inputFile, IsOutputPath, OutputPath);

            if (Mode != GrepMode.Grep)
            {
                if (OutputEncoding == null)
                {
                    if (OutputEncodingName == OutputEncodingMode.None)
                    {
                        OutputEncoding = InputEncoding;
                    }
                    else
                    {
                        OutputEncoding = Encoding.GetEncoding((int)OutputEncodingName);
                    }
                }
            }
            if (Mode == GrepMode.Grep ||
                Mode == GrepMode.Replace ||
                Mode == GrepMode.Delete)
            {
                if (Regex == null)
                {
                    Regex = GetRegex(SearchValue, IsWord, IsIgnoreCase, IsConditions, ConditionsMode);
                }
            }
            string outputLineEnd = null;
            if (Mode == GrepMode.CharCode)
            {
                outputLineEnd = GetOutputLineEnd(inputLineEnd, OutputLineEnd);
            }
            else
            {
                outputLineEnd = inputLineEnd;
            }

            if (Mode == GrepMode.Grep)
            {
                RunGrep(inputFile, InputEncoding, inputLineEnd, Regex);
            }
            else if (Mode == GrepMode.Replace)
            {
                if (IsConditions && ConditionsMode == ConditionsMode.Regex)
                {
                    if (CacheReplacementValue == null)
                    {
                        CacheReplacementValue = Regex.Unescape(ReplacementValue);
                    }
                }
                else
                {
                    if (CacheReplacementValue == null)
                    {
                        CacheReplacementValue = ReplacementValue;
                    }
                }
                RunReplace(inputFile, InputEncoding, Regex, IsRepeart, CacheReplacementValue, outputFile);
            }
            else if (Mode == GrepMode.Delete)
            {
                RunDelete(inputFile, InputEncoding, inputLineEnd, Regex, IsRepeart, outputFile);
            }
            else if (Mode == GrepMode.CharCode)
            {
                RunCharCodeConvert(inputFile, InputEncoding, inputLineEnd, outputFile, outputLineEnd, OutputEncoding);
            }
            else if (Mode == GrepMode.Indent)
            {
                RunIndentConvert(inputFile, InputEncoding, inputLineEnd, IndentConvertMode, TabSize, outputFile);
            }
            else if (Mode == GrepMode.Trim)
            {
                RunTrim(inputFile, InputEncoding, inputLineEnd, outputFile);
            }
        }
        #endregion

        #region RunGrep
        /// <summary>
        /// 指定した入力ファイル、エンコーディング、改行コード、正規表現でGrepを実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル</param>
        /// <param name="inputEncoding">エンコーディング</param>
        /// <param name="inputLineEnd">改行コード</param>
        /// <param name="regex">正規表現</param>
        internal void RunGrep(string inputFile, Encoding inputEncoding, string inputLineEnd, Regex regex)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (inputLineEnd == null)
            {
                throw new ArgumentNullException("inputLineEnd");
            }
            if (regex == null)
            {
                throw new ArgumentNullException("regex");
            }
            FileInfo info = new FileInfo(inputFile);
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                text = reader.ReadToEnd();
            }
            List<GrepMatche> grepMatches = new List<GrepMatche>();
            MatchCollection matches = regex.Matches(text);
            foreach (Match match in matches)
            {
                var columnIndex = 0;
                var lineNumber = 1;
                string lineText = null;
                for (int idx = 0; idx < text.Length; idx++)
                {
                    char ch = text[idx];
                    if (inputLineEnd == "\r")
                    {
                        if (ch == '\r')
                        {
                            lineNumber++;
                            columnIndex = 0;
                        }
                        else
                        {
                            columnIndex++;
                        }
                    }
                    else
                    {
                        if (ch == '\n')
                        {
                            lineNumber++;
                            columnIndex = 0;
                        }
                        else
                        {
                            columnIndex++;
                        }
                    }
                    if (idx == match.Index)
                    {
                        var textArray = text.Replace("\r\n", "\n").Replace("\r", "\n").Split(new char[] { '\n' });
                        lineText = textArray[lineNumber - 1];
                        break;
                    }
                }
                grepMatches.Add(new GrepMatche(match.Index, match.Length, lineNumber, columnIndex, lineText));
            }
            if (grepMatches.Count > 0)
            {
                RaiseGreped(new GrepedEventArgs(inputFile, info.Length, inputEncoding.EncodingName, grepMatches));
            }
        }
        #endregion

        #region RunReplace
        /// <summary>
        /// 指定した入力ファイル名、入力エンコーディング、正規表現、反復、置換文字列、出力先変更、出力ファイル名で置換処理を実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル名</param>
        /// <param name="inputEncoding">入力エンコーディング</param>
        /// <param name="regex">正規表現</param>
        /// <param name="isRepeart">反復</param>
        /// <param name="replacementValue">置換文字列</param>
        /// <param name="outputFile">出力ファイル名</param>
        internal void RunReplace(string inputFile, Encoding inputEncoding, Regex regex, bool isRepeart, string replacementValue, string outputFile)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (regex == null)
            {
                throw new ArgumentNullException("regex");
            }
            if (replacementValue == null)
            {
                throw new ArgumentNullException("replacementValue");
            }
            if (outputFile == null)
            {
                throw new ArgumentNullException("outputFile");
            }
            string oldText = null;
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                oldText = reader.ReadToEnd();
                text = oldText;
            }
            int count = 0;
            if (isRepeart)
            {
                while (regex.IsMatch(text))
                {
                    count += regex.Matches(text).Count;
                    text = regex.Replace(text, replacementValue);
                }
            }
            else
            {
                count += regex.Matches(text).Count;
                text = regex.Replace(text, replacementValue);
            }
            if (inputFile != outputFile || oldText != text)
            {
                FileInfo info = new FileInfo(outputFile);
                if (!info.Directory.Exists)
                {
                    info.Directory.Create();
                }
                if (oldText == text)
                {
                    File.Copy(inputFile, outputFile, true);
                }
                else
                {
                    using (var writer = new StreamWriter(outputFile, false, inputEncoding))
                    {
                        writer.Write(text);
                    }
                    RaiseReplaced(new ReplacedEventArgs(outputFile, info.Length, inputEncoding.EncodingName, count));
                }
            }
        }
        #endregion

        #region RunDelete
        /// <summary>
        /// 指定した入力ファイル名、入力エンコーディング、入力改行コード、正規表現、反復、出力先変更、出力ファイル名で、削除処理を実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル名</param>
        /// <param name="inputEncoding">入力エンコーディング</param>
        /// <param name="inputLineEnd">入力改行コード</param>
        /// <param name="regex">正規表現</param>
        /// <param name="isRepeart">反復</param>
        /// <param name="outputFile">出力ファイル名</param>
        internal void RunDelete(string inputFile, Encoding inputEncoding,string inputLineEnd, Regex regex, bool isRepeart, string outputFile)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (regex == null)
            {
                throw new ArgumentNullException("regex");
            }
            if (outputFile == null)
            {
                throw new ArgumentNullException("outputFile");
            }
            string oldText = null;
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                oldText = reader.ReadToEnd();
                text = oldText;
            }
            bool isEof = IsEof(text);
            int count = 0;
            if (isRepeart)
            {
                while (regex.IsMatch(text))
                {
                    count += regex.Matches(text).Count;
                    text = regex.Replace(text, string.Empty);
                }
            }
            else
            {
                count = regex.Matches(text).Count;
                text = regex.Replace(text, string.Empty);
            }

            if (inputFile != outputFile || oldText != text)
            {
                string[] inputBuffers = text.Replace("\r\n", "\n").Replace("\r", "\n").Split(new char[] { '\n' });
                Write(inputBuffers, outputFile, inputEncoding, inputLineEnd, isEof);
                FileInfo info = new FileInfo(outputFile);
                RaiseDeleted(new DeletedEventArgs(outputFile, info.Length, inputEncoding.EncodingName, count));
            }
        }
        #endregion

        #region RunCharCodeConvert
        /// <summary>
        /// 指定した入力ファイル名、入力エンコーディング、入力改行コード、出力先変更、出力ファイル名、出力改行コード、出力エンコーディングで文字コード変換を実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル名</param>
        /// <param name="inputEncoding">入力エンコーディング</param>
        /// <param name="inputLineEnd">入力改行コード</param>
        /// <param name="outputFile">出力ファイル名</param>
        /// <param name="outputLineEnd">出力改行コード</param>
        /// <param name="outputEncoding">出力エンコーディング</param>
        internal void RunCharCodeConvert(string inputFile, Encoding inputEncoding, string inputLineEnd, string outputFile, string outputLineEnd, Encoding outputEncoding)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (inputLineEnd == null)
            {
                throw new ArgumentNullException("inputLineEnd");
            }
            if (outputFile == null)
            {
                throw new ArgumentNullException("outputFile");
            }
            if (outputEncoding == null)
            {
                throw new ArgumentNullException("outputEncoding");
            }
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                text = reader.ReadToEnd();
            }
            bool isEof = IsEof(text);
            string[] inputBuffers = text.Replace("\r\n", "\n").Replace("\r", "\n").Split(new char[] { '\n' });

            bool isEncodingConvert = false;
            bool isLineEndConvert = false;
            if (inputEncoding != outputEncoding)
            {
                isEncodingConvert = true;
            }
            if (inputLineEnd != outputLineEnd)
            {
                isLineEndConvert = true;
            }
            if (inputFile != outputFile || isEncodingConvert || isLineEndConvert)
            {
                Write(inputBuffers, outputFile, outputEncoding, outputLineEnd, isEof);
                FileInfo info = new FileInfo(outputFile);
                RaiseCharCodeConverted(new CharCodeConvertedEventArgs(outputFile, info.Length, outputEncoding.EncodingName, isEncodingConvert, isLineEndConvert));
            }
        }
        #endregion

        #region RunIndentConvert
        /// <summary>
        /// 指定した入力ファイル名、入力エンコーディング、入力改行コード、変換モード、タブサイズ、出力先変更、出力ファイル名、出力改行コード、出力エンコーディングでインデント変換を実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル名</param>
        /// <param name="inputEncoding">入力エンコーディング</param>
        /// <param name="inputLineEnd">入力改行コード</param>
        /// <param name="mode">変換モード</param>
        /// <param name="tabSize">タブサイズ</param>
        /// <param name="outputFile">出力ファイル名</param>
        internal void RunIndentConvert(string inputFile, Encoding inputEncoding, string inputLineEnd, IndentConvertMode mode, int tabSize, string outputFile)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (inputLineEnd == null)
            {
                throw new ArgumentNullException("inputLineEnd");
            }
            if (tabSize < 2)
            {
                throw new ArgumentException("タブサイズは2以上を指定して下さい。");
            }
            if (outputFile == null)
            {
                throw new ArgumentNullException("outputFile");
            }
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                text = reader.ReadToEnd();
            }
            bool isEof = IsEof(text);
            string[] inputBuffers = text.Replace("\r\n", "\n").Replace("\r", "\n").Split(new char[] { '\n' });

            var outputBuffers = new List<string>();
            string outputLine = null;
            int count = 0;
            for (int idx = 0; idx < inputBuffers.Length - 1; idx++)
            {
                if (mode == IndentConvertMode.Tab)
                {
                    outputLine = GrepHelper.GetIndentToTab(inputBuffers[idx], tabSize);
                }
                else if (mode == IndentConvertMode.Space)
                {
                    outputLine = GrepHelper.GetIndentToSpace(inputBuffers[idx], tabSize);
                }
                if (outputLine != inputBuffers[idx])
                {
                    count++;
                }
                outputBuffers.Add(outputLine);
            }

            if (inputFile != outputFile || count > 0)
            {
                Write(outputBuffers.ToArray(), outputFile, inputEncoding, inputLineEnd, isEof);
                FileInfo info = new FileInfo(outputFile);
                RaiseIndentConverted(new IndentConvertedEventArgs(outputFile, info.Length, inputEncoding.EncodingName, count));
            }
        }
        #endregion

        #region RunTrim
        /// <summary>
        /// 指定した入力ファイル名、入力エンコーディング、入力改行コード、出力先変更、出力ファイル名でTrim処理を実行します。
        /// </summary>
        /// <param name="inputFile">入力ファイル</param>
        /// <param name="inputEncoding">入力エンコーディング</param>
        /// <param name="inputLineEnd">入力改行コード</param>
        /// <param name="outputFile">出力ファイル名</param>
        internal void RunTrim(string inputFile, Encoding inputEncoding, string inputLineEnd, string outputFile)
        {
            if (inputFile == null)
            {
                throw new ArgumentNullException("inputFile");
            }
            if (!File.Exists(inputFile))
            {
                throw new FileNotFoundException("ファイルが見つかりません。", inputFile);
            }
            if (inputEncoding == null)
            {
                throw new ArgumentNullException("inputEncoding");
            }
            if (inputLineEnd == null)
            {
                throw new ArgumentNullException("inputLineEnd");
            }
            if (outputFile == null)
            {
                throw new ArgumentNullException("outputFile");
            }
            string text = null;
            using (var reader = new StreamReader(inputFile, inputEncoding))
            {
                text = reader.ReadToEnd();
            }
            bool isEof = IsEof(text);
            string[] inputBuffers = text.Replace("\r\n", "\n").Replace("\r", "\n").Split(new char[] { '\n' });

            var outputBuffers = new List<string>();
            string outputLine = null;
            int count = 0;
            for (int idx = 0; idx < inputBuffers.Length - 1; idx++)
            {
                outputLine = inputBuffers[idx].TrimEnd();
                if (inputBuffers[idx] != outputLine)
                {
                    count++;
                }
                outputBuffers.Add(outputLine);
            }

            if (inputFile != outputFile || count > 0)
            {
                Write(outputBuffers.ToArray(), outputFile, inputEncoding, inputLineEnd, isEof);
                FileInfo info = new FileInfo(outputFile);
                RaiseLineTrim(new LineTrimConvertedEventArgs(outputFile, info.Length, inputEncoding.EncodingName, count));
            }
        }
        #endregion

        #region IsEof
        private static Regex m_eofCRLF = new Regex("\r\n$", RegexOptions.None);
        private static Regex m_eofCR = new Regex("\r$", RegexOptions.None);
        private static Regex m_eofLF = new Regex("\n$", RegexOptions.None);

        /// <summary>
        /// 指定したテキストからファイル終端有無を取得します。
        /// </summary>
        /// <param name="text">テキスト</param>
        /// <returns>trueの場合はファイル終端有、falseの場合はファイル終端無</returns>
        public static bool IsEof(string text)
        {
            if (m_eofCRLF.IsMatch(text) ||
                m_eofCR.IsMatch(text) ||
                m_eofLF.IsMatch(text))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion

        #region Write
        /// <summary>
        /// 指定したコンテキスト、出力ファイル、エンコーディング、改行、ファイル終端有無でファイルに出力します。
        /// </summary>
        /// <param name="outputBuffers">コンテキスト</param>
        /// <param name="outputFile">出力ファイル</param>
        /// <param name="encoding">エンコーディング</param>
        /// <param name="lineEnd">改行</param>
        /// <param name="isEof">ファイル終端有無</param>
        private static void Write(string[] outputBuffers, string outputFile, Encoding encoding, string lineEnd, bool isEof)
        {
            FileInfo info = new FileInfo(outputFile);
            if (!info.Directory.Exists)
            {
                info.Directory.Create();
            }

            using (var writer = new StreamWriter(outputFile, false, encoding))
            {
                for (int idx = 0; idx < outputBuffers.Length; idx++)
                {
                    writer.Write(outputBuffers[idx]);
                    if (idx < outputBuffers.Length)
                    {
                        writer.Write(lineEnd);
                    }
                    else
                    {
                        if (isEof)
                        {
                            writer.Write(lineEnd);
                        }
                    }
                }
            }
        }
        #endregion

        #region OnProgress
        /// <summary>
        /// 進行状況ファイルイベント
        /// </summary>
        public event EventHandler<ProgressEventArgs> OnProgress;
        /// <summary>
        /// 進行状況ファイルイベントを実行します。
        /// </summary>
        /// <param name="e">イベントデータ</param>
        internal void RaiseProgressFile(ProgressEventArgs e)
        {
            if (OnProgress != null)
            {
                OnProgress(this, e);
            }
        }
        #endregion
        
        #region OnGreped
        /// <summary>
        /// 検索イベント
        /// </summary>
        public event EventHandler<GrepedEventArgs> OnGreped;
        /// <summary>
        /// 検索イベントを実行します。
        /// </summary>
        /// <param name="e">イベントデータ</param>
        internal void RaiseGreped(GrepedEventArgs e)
        {
            if (OnGreped != null)
            {
                OnGreped(this, e);
            }
        }
        #endregion

        #region OnReplaced
        /// <summary>
        /// 置換イベント
        /// </summary>
        public event EventHandler<ReplacedEventArgs> OnReplaced;
        /// <summary>
        /// 置換イベントを実行します。
        /// </summary>
        /// <param name="e">イベントデータ</param>
        internal void RaiseReplaced(ReplacedEventArgs e)
        {
            if (OnReplaced != null)
            {
                OnReplaced(this, e);
            }
        }
        #endregion

        #region OnDeleted
        /// <summary>
        /// 削除イベント
        /// </summary>
        public event EventHandler<DeletedEventArgs> OnDeleted;
        /// <summary>
        /// 削除イベントを実行します。
        /// </summary>
        /// <param name="e">イベントデータ</param>
        internal void RaiseDeleted(DeletedEventArgs e)
        {
            if (OnDeleted != null)
            {
                OnDeleted(this, e);
            }
        }
        #endregion

        #region OnCharCodeConverted
        /// <summary>
        /// 文字コード変換イベント
        /// </summary>
        public event EventHandler<CharCodeConvertedEventArgs> OnCharCodeConverted;
        /// <summary>
        /// 文字コード変換イベントを実行します。
        /// </summary>
        /// <param name="e"></param>
        internal void RaiseCharCodeConverted(CharCodeConvertedEventArgs e)
        {
            if (OnCharCodeConverted != null)
            {
                OnCharCodeConverted(this, e);
            }
        }
        #endregion

        #region OnIndentConverted
        /// <summary>
        /// インデント変換イベント
        /// </summary>
        public event EventHandler<IndentConvertedEventArgs> OnIndentConverted;
        /// <summary>
        /// インデント変換イベントを実行します。
        /// </summary>
        /// <param name="e">イベントデータ</param>
        internal void RaiseIndentConverted(IndentConvertedEventArgs e)
        {
            if (OnIndentConverted != null)
            {
                OnIndentConverted(this, e);
            }
        }
        #endregion

        #region OnLineTrim
        /// <summary>
        /// 行末Trimイベント
        /// </summary>
        public event EventHandler<LineTrimConvertedEventArgs> OnLineTrim;
        /// <summary>
        /// 行末Trimイベントを実行します。
        /// </summary>
        /// <param name="e"></param>
        internal void RaiseLineTrim(LineTrimConvertedEventArgs e)
        {
            if (OnLineTrim != null)
            {
                OnLineTrim(this, e);
            }
        }
        #endregion

        #region OnCompleted
        /// <summary>
        /// 完了イベントを実行します。
        /// </summary>
        public event EventHandler<CompletedEventArgs> OnCompleted;
        internal void RaiseCompleted(CompletedEventArgs e)
        {
            if (OnCompleted != null)
            {
                OnCompleted(this, e);
            }
        }
        #endregion

        #region GetRegex
        /// <summary>
        /// 指定した検索文字列、単語単位、大文字小文字区別、条件、条件モードで、正規表現を取得します。
        /// <param name="searchValue">検索文字列</param>
        /// <param name="isWord">単語単位</param>
        /// <param name="isIgnoreCase">大文字小文字区別</param>
        /// <param name="isConditions">条件</param>
        /// <param name="conditionMode">条件モード</param>
        /// <returns>正規表現</returns>
        /// </summary>
        public static Regex GetRegex(string searchValue, bool isWord, bool isIgnoreCase, bool isConditions, ConditionsMode conditionMode)
        {
            if (searchValue == null)
            {
                throw new ArgumentNullException("searchValue");
            }
            if (string.IsNullOrEmpty(searchValue))
            {
                throw new ArgumentException("searchValue");
            }
            Regex regex = null;
            string searchVal = null;
            if (isConditions)
            {
                if (conditionMode == ConditionsMode.Regex)
                {
                    if (isWord)
                    {
                        searchVal = GrepHelper.GetWordRegex(searchValue);
                    }
                    else
                    {
                        searchVal = searchValue;
                    }
                }
                else if (conditionMode == ConditionsMode.WildCard)
                {
                    if (isWord)
                    {
                        searchVal = GrepHelper.GetWordRegex(GrepHelper.GetWildCardRegex(searchValue));
                    }
                    else
                    {
                        searchVal = GrepHelper.GetWildCardRegex(searchValue);
                    }
                }
            }
            else
            {
                if (isWord)
                {
                    searchVal = GrepHelper.GetWordRegex(Regex.Escape(searchValue));
                }
                else
                {
                    searchVal = Regex.Escape(searchValue);
                }
            }
            if (isIgnoreCase)
            {
                regex = new Regex(searchVal, RegexOptions.Multiline | RegexOptions.IgnoreCase);
            }
            else
            {
                regex = new Regex(searchVal, RegexOptions.Multiline);
            }
            return regex;
        }
        #endregion

    }
}
